package Graphics::PlanarVector::SubPath;
use strict;
use Graphics::PlanarVector::Vector qw/vector veq/;
use Graphics::PlanarVector::PathSegment 'path_segment';
use List::Util 'reduce';
use Exporter 'import';
use Carp;
our @EXPORT_OK = qw/subpath bbox intersect/;

use fields qw/kids start closed bbox/;

use overload (
	'""'	=> sub {
		my ($sp, $ret) = (shift, "\tsubpath {\n");
		if (!@{$sp->{kids}}) {
			return "";
		}
		for ($sp->kids) {
			$ret .= "\t\t$_\n"
		}
		if ($sp->{closed}) {
			$ret.= "\t\tclosed\n";
		}
		$ret."\t}\n";
	},
);

sub subpath {
	bless { kids=>[], start=>shift//vector(0,0) }
}

sub dup {
	my $self = shift;
	bless { kids=>[ map { $_->dup } @{$self->{kids}} ],
		bbox=>$self->{bbox},
		closed=>$self->{closed},
		unwind=>$self->{unwind}
	}
}

sub transform {
	my ($sp, $m) = @_;
	my $r = subpath();
	@{$r->{kids}} = map { $_->transform($m) } $sp->kids;
	$r->{closed} = $sp->{closed};
	$r
}

sub reverse {
	my $sp = shift;
	bless {
		kids	=> [reverse map { $_->reverse } $sp->kids],
		start	=> $sp->{start},
		closed	=> $sp->{closed},
	}
}

sub current_point {
	my $sp = shift;
	return @{$sp->{kids}} ? $sp->{kids}[-1]{pt}[-1] : $sp->{start}
}

sub add_segment {
	my $sp = shift;
	return if (@_ == 2 && veq(@_));
	push @{$sp->{kids}}, &path_segment;
}

sub close_sub {
	my $sp = shift;
	return if $sp->{closed};

	my $seg = $sp->{kids}[-1] or return;
	$sp->{closed} = 1;
	
	$sp->{start} = $sp->{kids}[0]{pt}[0];

	if (veq($seg->{pt}[-1], $sp->{start})) {
		$seg->{pt}[-1] = $sp->{start}
	} else {
		add_segment($sp, $seg->{pt}[-1], $sp->{start})
	}
	$sp->{start}
}

sub bbox {
	my $sp = shift;
	$sp->{bbox} //= reduce { $a | $b } map { $_->bbox } $sp->kids
}

sub kids { @{shift->{kids}} }

my $common = \&Math::Util::Range::common;
sub intersect {
	while (@_) {
		my $this = shift;
		for my $that (@_) {
			next unless $this->bbox & $that->bbox;

			if ($this->{kids}) {
				for my $s (@{$this->{kids}}) {
					$s->intersect($that)
				}
			} else {
				for my $s (@{$that->{kids}}) {
					$s->intersect($this)
				}
			}
		}
	}
}

sub self_intersect {
	my $sp = shift;
	return $sp if $sp->{nowind};

	my @segs = $sp->kids;
	$sp->{kids} = [];
	while (my $this = shift @segs) {
		push @{$sp->{kids}}, $this;
		$this->self_intersect;

		for (@segs) {
			$this->intersect($_);
		}
	}
	$sp;
}

sub cleanup {
	my $sp = shift;
	@{$sp->{kids}} = grep {
		@{$_->{pt}} > 2 or
		@{$_->{pt}} == 2 && $_->{pt}[0]->dist2($_->{pt}[1]) > 1e-6
	} @{$sp->{kids}};
	$sp->{start} = $sp->{kids}[0]{pt}[0];
}

1;
